#include "LightMind.h"
#include "TouchInput.h"
#include "Ground.h"
#include "Character.h"
#include "NPC.h"
#include "rapidxml.hpp"
#include "CharacterCL.h"
#include "ControlBar.h"
#include "Ball.h"
#include "BallListener.h"
#include "Clouds.h"
#include "Monster.h"
#include "LanternGame.h"
#include "BreatheGame.h"
#include "BlankGame.h"
#include <iostream>
#include <fstream>
#include <sstream>
#define _USE_MATH_DEFINES
#include <math.h>

//#include "Glew/GL/glew.h"
//
//#include "SFML/Graphics/Shader.hpp"
//#include "sfml/OpenGL.hpp"
//#include "sfml/Graphics.hpp"
//
//
//
//#include <LTBL/Light/LightSystem.h>
//#include <LTBL/Light/Light_Point.h>
//#include <LTBL/Utils.h>

#include <Thor/Particles.hpp>
#include <Thor/Animation.hpp>
#include <Thor/Vectors/PolarVector.hpp>
#include <Thor/Math/Distributions.hpp>



LightMind::LightMind(void)
{
}


LightMind::~LightMind(void)
{
}



void LightMind::createWindow()
{
	sf::ContextSettings settings;
	settings.antialiasingLevel = 4;
	window.create(sf::VideoMode(592, 955/*1255*/), "Light Mind", sf::Style::Close, settings);
}

bool LightMind::run()
{

	createWindow();


	


	touch.Init();

	/*sf::View view(sf::Vector2f(window.getCenterX(), window.getCenterY()), sf::Vector2f(window.getCenterX()*2, window.getCenterY()*2));
	view.setRotation(270);
	window.setView(view);*/
 
	State state = TITLE;

	
	/**
		Return state from the function when you wish to change the state
	**/
    while (window.isOpen())
    {
		switch(state)
		{
			case TITLE:
				state = runTitle();
				break;
			case NAVIGATOR:
				state = runNavigator();
				break;

		}

		 
        
    }

	return false;
}


State LightMind::runNavigator()
{

	int aa = window.getSettings().antialiasingLevel;


	window.setFramerateLimit(30);
	b2Vec2 gravity(0.f, 9.8f);
	b2World world(gravity);

	


	Ground ground(&world, &window);

	Character character(&world, &window);

	CharacterCL cInstance;
	cInstance.init(&character);

	world.SetContactListener(&cInstance);

	readInNPCs(&world, &window, &ground);

	ControlBar cbar(&window);

	currentNPC = - 1;

	Control state = NOTHING;

	std::vector<int> npcOrder;

	/**
	PARTICLE TEST
	**/
	// Load image and initialize sprite
	std::shared_ptr<sf::Texture> texture(new sf::Texture());
	texture->loadFromFile("particle.png");
		

	// Create emitter

	thor::UniversalEmitter::Ptr emitters[4];
	thor::ParticleSystem system(texture);

	for (int i = 0; i < 4; i++)
	{

		emitters[i] = thor::UniversalEmitter::create();
		emitters[i]->setEmissionRate(20.f);
		emitters[i]->setParticleLifetime(sf::seconds(3.f));
	

		// Create particle system
	
		system.addEmitter(emitters[i]);

	}

	thor::ColorGradient gradient = thor::createGradient(sf::Color(97, 181, 236)) (2.5f) (sf::Color(97, 181, 255));


	thor::FadeAnimation fader(0.1f, 0.1f);
	thor::ColorAnimation colouriser(gradient);

	system.addAffector(thor::AnimationAffector::create(colouriser));
	system.addAffector( thor::AnimationAffector::create(fader) );
	system.addAffector( thor::TorqueAffector::create(20.f) );
	system.addAffector( thor::ForceAffector::create(sf::Vector2f(1.f, 0.f))  );

	



	// Attributes that influence emitter
	sf::Vector2f		position;
	//thor::PolarVector2f velocity(10.f, 00.f);
	bool				paused = false;

	sf::Clock frameClock;

	/**
	PARTICLE TEST END
	**/


	while (window.isOpen())
    {
		sf::Event event;
		while (window.pollEvent(event))
		{
			sf::Vector2i loc;

			switch (event.type)
			{
				case sf::Event::Closed:
					window.close();
					break;
				case sf::Event::MouseButtonPressed:
					loc = sf::Vector2i(window.convertCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
					if (loc.y > 300)
					{
						character.jump();
					}
					if (cbar.checkClicked(loc))
					{
						Control control = cbar.clicked(&npcOrder, npcs);
						if (control == EXIT)
						{
							return TITLE;
						}
					}
					break;
			}
		}
		if (state != CLEAR && state != PAUSE)
		{
			
			addCharacterMovement(&character);
			int npcCount = 0;
			for (int i = 0; i < numNPCs; i++)
			{
				if (!npcs[i].isComplete())
				{
					npcCount++;
					if (character.checkNPC(&npcs[i], true))
					{
						currentNPC = i;
						npcOrder.push_back(currentNPC);
						runGame(&npcs[i]);
						character.unlockMovement();
						currentNPC = -1;
						npcs[i].finishNPC();

						character.setPosition(sf::Vector2f(character.getPosition().x+200, character.getPosition().y -50));

					}
				}
			
			}
			if (npcCount == 0)
			{
				return TITLE;
			}


			float step = 1.0f/30.0f;
		
			world.Step(step, 20, 20);

			windowOffset(&character);

		}
		const sf::Time frameTime = frameClock.restart();
		system.update(frameTime);
		window.clear(window.getBackground()->getBackground());
		for (int i = 0; i < 3; i++)
		{
			emitters[i]->setParticlePosition(thor::Distributions::circle(sf::Vector2f(npcs[i].getPosition().x, npcs[i].getPosition().y), npcs[i].getHeight()/2));
		}
		if (state != CLEAR)
		{

			ground.drawGround();
			character.drawCharacter();
			for (int i = 0; i < numNPCs; i++)
			{
				npcs[i].drawNPC();
			}
		}
		window.draw(system);
		cbar.drawBar();
		window.display();

	}
	return TITLE;
}

State LightMind::runGame(NPC* oldNpc)
{

	b2Vec2 gravity(0.f, 9.8f);
	b2World world(gravity);
	Character character(&world, &window);

	CharacterCL cInstance;
	cInstance.init(&character);

	world.SetContactListener(&cInstance);

	window.addOffset(-window.getOffset());

	std::vector<MapBox> groundBoxes;
	MapBox box;
	float w = window.getWidth() - 100;
	float h = 50;
	float xLoc = window.getWidth()/2;
	float yLoc = 25;
	float groundHeight = window.getHeight();
	box.init(&window, &world, w, h, xLoc, yLoc, groundHeight);
	groundBoxes.push_back(box);

	MapBox topBox;
	w = window.getWidth()-100;
	h = 50;
	xLoc = window.getWidth()/2;
	yLoc = window.getHeight() - 150;
	groundHeight = window.getHeight();
	topBox.init(&window, &world, w, h, xLoc, yLoc, groundHeight);
	groundBoxes.push_back(topBox);

	MapBox leftBox;
	w = 50;
	h = window.getHeight()-125;
	xLoc = 25;
	yLoc = h/2;
	groundHeight = window.getHeight();
	leftBox.init(&window, &world, w, h, xLoc, yLoc, groundHeight);
	groundBoxes.push_back(leftBox);

	MapBox rightBox;
	w = 50;
	h = window.getHeight()-125;
	xLoc = window.getWidth()-25;
	yLoc = h/2;
	groundHeight = window.getHeight();
	rightBox.init(&window, &world, w, h, xLoc, yLoc, groundHeight);
	groundBoxes.push_back(rightBox);

	std::stringstream ss;
	ss << oldNpc->getGame();


	NPC npc = NPC(&world, &window, 150, window.getHeight()-50, oldNpc->getTitle(), oldNpc->getName(), oldNpc->getBlurb(), ss.str(), *oldNpc->getMessages(), false);

	Control state = NOTHING;

	character.setPosition(sf::Vector2f(100, window.getHeight()-50));

	ControlBar cbar(&window);

	BallListener listener;
	world.SetContactListener(&listener);

	


	bool message = false;

	Monster monster(&world, &window, window.getWidth()/2);

	npc.addMonster(&monster);

	bool monsterDestroyed = false;

	
	Clouds clouds(&monster);


	LanternGame lanternGame;
	BreatheGame breatheGame;
	BlankGame blankGame;

	Game * game;

	if (npc.getGame() == 1)
	{
		game = &lanternGame;
	}
	else if (npc.getGame() == 2)
	{
		game = &breatheGame;
	}
	else
	{
		game = &blankGame;
	}

	game->initialise(&window, &clouds, &character, &world, &npc, &monster);


	while (window.isOpen())
    {
		sf::Event event;
		while (window.pollEvent(event))
		{
			sf::Vector2i loc;

			switch (event.type)
			{
				case sf::Event::Closed:
					window.close();
					break;
				case sf::Event::MouseButtonPressed:
					loc = sf::Vector2i(window.convertCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
					if (loc.y > 300)
					{
						character.jump();
						if (message)
						{
							npc.nextMessage();
						}
						game->mousePressed(sf::Vector2f(loc));
					}

					if (cbar.checkClicked(loc))
					{
						Control temp = state;
						state = cbar.clicked(&npc);
						if (state == EXIT)
						{
							return NAVIGATOR;
						}
						else if (state == temp && state == CLEAR)
						{
							state = NOTHING;
						}
					}
					break;
			}
		}
		if (state != CLEAR && state != PAUSE)
		{
			if (character.checkNPC(&npc, false))
			{
				character.stopMovement();
				message = true;
			}
			if (character.canMove())
			{
				addCharacterMovement(&character);
			}
			float step = 1.0f/30.0f;
		
			world.Step(step, 20, 20);

		}


		if (npc.isComplete())
		{
			message = false;
			character.unlockMovement();
			currentNPC = -1;
			if (!npc.isFight())
			{
				return NAVIGATOR;
			}		
			game->startPlaying();
		}
		

		window.clear(window.getBackground()->getBackground());
		


		if (state != CLEAR)
		{
			game->performLogic();
			game->draw();

			character.drawCharacter();
			for (int i = 0; i < groundBoxes.size(); i++)
			{
				groundBoxes[i].drawBox(&window, window.getHeight(),sf::Color(60, 60, 60, 255));
			}
			npc.drawNPC();
			if (!monsterDestroyed)
				monsterDestroyed = monster.drawMonster();
			else if (!message && monsterDestroyed)
				monsterDestroyed = monster.drawMonster();
			if (monsterDestroyed && !message)
			{
				return NAVIGATOR;
			}
			if (message && !npc.isComplete())
			{
				if (npc.getCurrentMessage()->getFight() != -1)
				{
					clouds.drawClouds(&window);
				}
			}
		}
		

		cbar.drawBar();
		
		window.display();

	}
	return NAVIGATOR;
}

void LightMind::windowOffset(Character* character)
{
	int moveValue = 200;

	if (character->getPosition().x + moveValue >= window.getOffset()+window.getWidth())
	{
		window.addOffset(character->getPosition().x+moveValue-(window.getOffset()+window.getWidth()));
	}
	else if (character->getPosition().x - moveValue <= window.getOffset())
	{
		window.addOffset(character->getPosition().x-moveValue-(window.getOffset()));
	}
}

void LightMind::addCharacterMovement(Character* character)
{
	character->addForce(sf::Mouse::getPosition(window));
}

State LightMind::runTitle()
{
	
	// Create a text
	sf::Text title("Light Mind", *window.getFont());
	title.setCharacterSize(50);
	title.setColor(sf::Color::White);
	sf::FloatRect titleRect = title.getLocalBounds();
	sf::Vector2f center = window.getCenter();
	title.setPosition(window.getCenter().x-(titleRect.width/2), window.getCenter().y-(titleRect.height/2));

	LMButton play = LMButton("Play", sf::Vector2f(), window.getFont());
	sf::FloatRect playRect = play.getBox().getLocalBounds();
	play.setPosition(sf::Vector2f(window.getCenter().x-(playRect.width/2), window.getCenter().y+titleRect.height));
	
	LMButton exit = LMButton("Exit", sf::Vector2f(), window.getFont());
	sf::FloatRect exitRect = exit.getBox().getLocalBounds();
	exit.setPosition(sf::Vector2f(window.getCenter().x-(exitRect.width/2), play.getPosition().y + exitRect.height));

	LMButton *buttons[2];
	buttons[0] = &play;
	buttons[1] = &exit;

	window.addOffset(-window.getOffset());

	while (window.isOpen())
    {
		sf::Event event;
		while (window.pollEvent(event))
		{
			sf::Vector2i loc;
			switch (event.type)
			{
				case sf::Event::Closed:
					window.close();
					break;
				case sf::Event::MouseButtonPressed:
					loc = sf::Vector2i(window.convertCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y))); 
					int i;
					for (i = 0; i < 2; i++)
					{
						buttons[i]->checkClicked(loc);
					}
					break;
				case sf::Event::MouseButtonReleased:
					if (exit.isClicked())
					{
						exit.releaseClicked();
						window.close();
					}
					else if (play.isClicked())
					{
						play.releaseClicked();
						return NAVIGATOR;
					}
					break;
			}
		}
		
		window.clear();
		// Draw it
		window.draw(title);

		int i;
		for (i=0; i < 2; i++)
		{
			buttons[i]->drawThis(&window);
		}




		window.display();
	}
	return NAVIGATOR;
}

void LightMind::closeApplication()
{
	window.close();
}

void LightMind::readInNPCs(b2World* wo, WindowManager* win, Ground* g)
{
	std::ifstream inFile;
	inFile.open("npcs.xml");

	int i;
	i = 0;
	std::string string;
	if (inFile.is_open())
	{
		while(inFile.good())
		{
			std::string line;
			std::getline(inFile, line);
			string = string + line;
		}
	}

	inFile.close();

	char* text = (char*) string.c_str();

	rapidxml::xml_document<> doc;
	doc.parse<0>(text);

	rapidxml::xml_node<>* node = doc.first_node("npcs");
	numNPCs = 0;
	for (rapidxml::xml_node<>* child = node->first_node("npc"); child; child = child->next_sibling())
	{
		numNPCs++;
	}

	npcs = new NPC[numNPCs];

	i = 0;

	for (rapidxml::xml_node<>* child = node->first_node("npc"); child; child = child->next_sibling())
	{
		rapidxml::xml_node<>* nodeNPC = child;

		rapidxml::xml_node<>* titleNode = nodeNPC->first_node("title");
		rapidxml::xml_node<>* blurbNode = nodeNPC->first_node("blurb");
		rapidxml::xml_node<>* nameNode = nodeNPC->first_node("name");

		rapidxml::xml_node<>* messagesNode = nodeNPC->first_node("messages");
		rapidxml::xml_node<>* gameNode = nodeNPC->first_node("game");
	
	
		std::string title = std::string(titleNode->value());
		std::string blurb = std::string(blurbNode->value());
		std::string name = std::string(nameNode->value());
		std::string game = std::string(gameNode->value());

	

		int count;
		count = 0;

		for (rapidxml::xml_node<>* child = messagesNode->first_node("message"); child; child = child->next_sibling())
		{
			count++;
		}




		int numMessages = count;

		std::vector<NPCMessage> messages;
		count = 0;
		for (rapidxml::xml_node<>* messageNode = messagesNode->first_node("message"); messageNode; messageNode = messageNode->next_sibling())
		{
			rapidxml::xml_node<>* stringNode = messageNode->first_node("string");
			rapidxml::xml_node<>* lightNode = messageNode->first_node("light");
			rapidxml::xml_node<>* fightNode = messageNode->first_node("fight");
			NPCMessage message(std::string(stringNode->value()), std::string(lightNode->value()), std::string(fightNode->value())); 
			messages.push_back(message);
			count++;
		}

		npcs[i] = NPC(wo, win, 1000.f + (i*1000.f), 500, title, name, blurb, game, messages, true);

		i++;
	}

}



